package ngat.oss;


import ngat.message.OSS.*;
import ngat.instrument.*;
import ngat.util.*;
import ngat.util.logging.*;
//import ngat.util.logging.test.*;
import ngat.astrometry.*;
import ngat.net.*;
import ngat.message.GUI_RCS.*;
import ngat.phase2.*;
//import ngat.sms.*;

import java.util.*;
import java.io.*;
import java.net.*;
import java.text.*;
import java.rmi.*;

import jyd.dbms.*;
import jyd.storable.*;
import jyd.collection.*;

/** 
 * This class is used to start up the OSS Transaction Server and associated
 * subsystems. Only one instance should be created at any time on a given 
 * machine. <br>
 * Oss opens the PhaseII database and starts the following Threads.
 * <dl>
 * <dt> DBServerThread(x 1):  
 * <dd> Services the TransactionRequests from user clients.
 * <dt> TSConnectThread(x N):  
 * <dd> Deal with communication between Server and Clients.
 * <dt> Monitor(x N):          
 * <dd> Send back reports to Control Client at regular intervals.
 * </dl>
 * User's TransactionRequests are stored in the TransactionQueue until serviced
 * on a first come first served basis but in priority order.
 *
 * $Id: Oss.java,v 1.7 2007/08/24 08:01:28 snf Exp snf $
 */   

public class Oss implements Logging {

    /** UTC TimeZone.*/
    public static SimpleTimeZone UTC = new SimpleTimeZone(0, "UTC");

    public static SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss z");

    public final static String DB_NAME = "LT_Phase2_001";

    /** Exit code to indicate RESTART of JVM.*/
    public static final int RESTART_CODE = 5;

    /** Exit code to indicate STOP of JVM.*/
    public static final int STOP_CODE = 4;

    /** Hook to allow the OSS to be attached to the ServerContext by
     * other Threads when they call startup() on this. */
    protected static Oss instance; 
    
    /** Reference to the PhaseII database file. */
    File databaseDir;    

    /** Rootname of database.*/
    String dbRootName;

    /** Reference to GlobalSettings. */
    GlobalSettings globalSettings;

    /** Transaction execution thread.*/
    protected QueryThread qt;

    /** JMS TrabsactionServer.*/
    protected TransactionServer st;

    /** The OSS identity - used in logging messages.*/
    String ossName;
  
    /** Start value for message ID.*/
    protected volatile long messageLong = 0L;

    /** TEA Scoring calculator.*/
    protected TestContention tscore;

    /** Charge accounting model.*/
    protected ChargingModel chargingModel; 

    /** Schedule monitor.*/
    protected DefaultScheduleMonitor schedMonitor;


    // Logging.
    Logger errorLog;       
    Logger traceLog;     
    Logger trafficLog;    
    Logger scheduleLog;   
    Logger performanceLog;
    Logger pestLog;
    Logger dtmsLog;

    /** Exit code.*/
    protected int exitCode;

    /** Determines whether the server is being rebooted now. */
    boolean rebooting;

    /** Determines the state of the TMX. */
    boolean paused;
    
    /** Determines the mode of shutdown. */
    boolean normalShutdown;

    /** Holds the user supplied command line overrides. these may be used by a
     * subsequent reboot() command, or may be modified by it.*/
    String[] argList;

    /** Hooks up resources and starts the Server. */
    public Oss(String dbRootName, String args[]) {

	instance = this;

	argList = args;

	this.dbRootName = dbRootName;

	
	startup(argList);

	// Launch this thread.
	traceLog.log(INFO, 1, "Starting OSS");

	sdf.setTimeZone(UTC);

	//TEMP #### ASSASSIN.
	//try {
	//  int uth = Integer.parseInt(System.getProperty("ASSASSIN"));
	//  (new Assassin(uth)).start();
	//} catch (Exception e) {
	//  System.err.println("** No assassin specified");
	//}
	    
	// DEBUG send imalives to SULs every minute or so
	while (true) {
	    try {
		try {Thread.sleep(60000L);} catch (InterruptedException ix) {}
		getScheduleMonitor().notifyListenersImalive();
	    } catch (Exception ee) {
		ee.printStackTrace();
	    }
	}

    } // end constructor [Oss].


    /** Hook into JVM execution thread.*/
    public static void main(String args[]) {
	if (args == null || args.length == 0) {
	    System.err.println("Usage: java Oss <context-File-Name> [<parameter-List>]");
	    return;
	}
	
	String dbRootName = System.getProperty("db.name", DB_NAME);

	try {
	    new Oss(dbRootName, args);
	} catch (Exception e) {
	    e.printStackTrace();
	    System.exit(1);
	}
    } // end (main).

    /** Returns the Oss main application (singleton) instance.*/
    public static Oss getInstance() { return instance; }

    /** Called to initialise all Server Environment variables and start up any
     * threads, sockets etc. */
    protected void startup(String[] args) {
	
	globalSettings = null;

	// Create the Loggers.
	traceLog       = LogManager.getLogger("TRACE");
	errorLog       = LogManager.getLogger("ERROR");
	trafficLog     = LogManager.getLogger("TRAFFIC");    
	scheduleLog    = LogManager.getLogger("SCHEDULE");   
	performanceLog = LogManager.getLogger("OSS");
	pestLog        = LogManager.getLogger("PEST");
	dtmsLog        = LogManager.getLogger("DTMS");

	traceLog.setLogLevel(ALL);
	errorLog.setLogLevel(OFF);
	trafficLog.setLogLevel(OFF);
	scheduleLog.setLogLevel(1);
	performanceLog.setLogLevel(1);
	pestLog.setLogLevel(OFF);
	dtmsLog.setLogLevel(OFF);

	// Default log  settings.
	traceLog.setUseGlobalHandlers(false); 
	errorLog.setUseGlobalHandlers(false); 
	trafficLog.setUseGlobalHandlers(false); 
	scheduleLog.setUseGlobalHandlers(false);
	performanceLog.setUseGlobalHandlers(false);
	pestLog.setUseGlobalHandlers(false);
	dtmsLog.setUseGlobalHandlers(false);

	// Console.
	LogHandler console = new ConsoleLogHandler(new BasicLogFormatter(180));
	console.setLogLevel(ALL);
	
	//errorLog.addHandler(console);
	try {
	    LogHandler errorFile = new FileLogHandler("/occ/logs/oss_error", new BasicLogFormatter(180), 
						      FileLogHandler.DAILY_ROTATION);
	    errorFile.setLogLevel(OFF);
	    errorLog.addHandler(errorFile);
	    traceLog.log(1, "Oss", ossName, "startup", "Created Error log file handler");
	} catch (IOException iox) {
	    errorLog.log(1, "Oss", ossName, "startup", "Could not create Error log file: "+iox);
	}
	
	//traceLog.addHandler(console);
	try {
	    LogHandler traceFile = new FileLogHandler("/occ/logs/oss_trace", new BasicLogFormatter(180), 
						      FileLogHandler.HOURLY_ROTATION);
	    traceFile.setLogLevel(ALL);
	    traceLog.addHandler(traceFile); 
	    traceLog.addHandler(console);
	    traceLog.log(1, "Oss", ossName, "startup", "Created Trace log file handler");
	} catch (IOException iox) {
	    errorLog.log(1, "Oss", ossName, "startup", "Could not create Trace log file: "+iox);
	}

	//dtmsLog.addHandler(console);
	
	//trafficLog.addHandler(console);
	performanceLog.addHandler(console);

	scheduleLog.addHandler(console);
	try {
	    LogHandler scheduleFile = new FileLogHandler("/occ/logs/oss_schedule", new BasicLogFormatter(180), 
							 FileLogHandler.HOURLY_ROTATION);
	    scheduleFile.setLogLevel(2);
	    scheduleLog.addHandler(scheduleFile); 
	    traceLog.log(1, "Oss", ossName, "startup", "Created Schedule log file handler");
	} catch (IOException iox) {
	    errorLog.log(1, "Oss", ossName, "startup", "Could not create Schedule log file: "+iox);
	}

// 	// Log to Mysql DB
// 	if (System.getProperty("log.mysql") != null) {
	    
// 	    System.err.println("Logging to mysql if you please...");
// 	    String scope = System.getProperty("scope", "TEST");
// 	    String loghost = System.getProperty("mysql.log.host", "localhost");
// 	    try {
// 		MysqlLogHandler sqlhandler = new MysqlLogHandler(scope, "OSS", loghost, "logs", "root", "appletart99ax");
// 		sqlhandler.setLogLevel(Logging.ALL);
// 		scheduleLog.addHandler(sqlhandler);
// 		traceLog.addHandler(sqlhandler);
// 		traceLog.log(1, "Oss", ossName, "startup", "Created Trace/Schedule log MySQL handler");
// 	    } catch (Exception e) {
// 		e.printStackTrace();
// 		errorLog.log(1, "Oss", ossName, "startup", "Could not create MySql Loghandler");
// 	    }
// 	}

	//performanceLog.addHandler(console);
	
	//pestLog.addHandler(console);
	try {
	    LogHandler pestFile = new FileLogHandler("/occ/logs/oss_pest", new BasicLogFormatter(180), 
						     FileLogHandler.DAILY_ROTATION);
	    pestFile.setLogLevel(OFF);
	    pestLog.addHandler(pestFile); 
	    traceLog.log(1, "Oss", ossName, "startup", "Created PEST log file handler");
	} catch (IOException iox) {
	    errorLog.log(1, "Oss", ossName, "startup", "Could not create PEST log file: "+iox);
	}

	// Parse the command line args. Remember to re-tokenize from arglist format.
	CommandParser parser = new CommandParser();
	try {
	    parser.parse(args);
	} catch (ParseException e) {
	    System.err.println("Failed to parse command line args.");
	    crashShutdown();
	}
	
	ConfigurationProperties map = parser.getTokenizedMap();
	
            
	// Set the context-file.
	String base = map.getProperty("base", System.getProperty("user.home"));
	String env  = map.getProperty("environment.file", "config/oss.environment");
	
	// Try to load global-settings. This has pointers to config files for
	// all the classes which provide configuration information.
	//
	// Scheduling (inc weights).
	// Client Settings (PEST).
	// Telescope Params.
	// Telescope Status (inc Instruments).
	
	try { 
	    ServerContext.loadContext(base, env);
	} catch (ServerEnvException se1) {
	    System.err.println("Failed to load context: \n"+se1);
	    crashShutdown();
	}

	// Setup the Environment Model Predictor ??
	//Scheduling.setSeeingPredictor(new StandardPredictor("OSS_Seeing_MODEL", 3, 3));

	//Scheduling.setPhotomPredictor(new StandardPredictor("OSS_Photom_MODEL", 2, 2));
	
	// Override settings with any command line args. NO NO NO NO NO NO
	globalSettings = ServerContext.getGlobalSettings();
	//map.remove("base");
	//map.remove("environment.file");
	//try {
	//   globalSettings.mergeProperties(map);
	//} catch (IllegalArgumentException ie) {
	//   System.err.println("Failed to override global settings: \n"+ie);
	//   crashShutdown();
	//}

	ossName = globalSettings.getOssName()+" V"+globalSettings.getOssVersion();;

	// Setup logging files as they are needed immediately.
	// ### TBD TBD TBD ###

	// OK to start logging now !
	
	traceLog.log(INFO, 1, "Oss", ossName, "startup", "Building Oss and parameter sets.");
	
	// Load the Client-authorization-table.
	traceLog.log(INFO, 1, "Generating transaction-authorization Table");
	try {
	    ServerContext.loadAuthorizationTable();
	    traceLog.log(INFO, 1, "Oss", ossName, "startup",
			 "Server-Context load Authorization-table ["+
			 globalSettings.getAuthorizationTableFile().getPath() + "] successful.");
	} catch (ServerEnvException se3) {
	    errorLog.log(ERROR, 1,
			 "Error loading authorization table.", se3);
	    errorLog.dumpStack(2, se3);
	    crashShutdown();
	}
	
	// Load the Password-table.
	traceLog.log(INFO, 1, "Oss", ossName, "startup", "Generating client-authentication data:");
	try {
	    ServerContext.loadPasswordTable();
	    traceLog.log(INFO, 1, "Oss", ossName, "startup",
			 "Server Context load Password-table [" +
			 globalSettings.getPasswordTableFile().getPath() + "] successful.");
	} catch (ServerEnvException se3) {
	    errorLog.log(ERROR, 1,
			 "Error loading password table.",se3);
	    errorLog.dumpStack(2, se3);
	    crashShutdown();
	}
	
	// Load the News-articles.
	traceLog.log(INFO, 1, "Oss", ossName, "startup", "Loading News-articles");
	try {
	    ServerContext.loadNews();
	    traceLog.log(INFO, 1, "Oss", ossName, "startup", 
			 "Server-Context load News [" +
			  ServerContext.getNewsFileName() + "] successful.");
	} catch (ServerEnvException se3) {
	    errorLog.log(ERROR, 1, "Error loading news.", se3);
	    errorLog.dumpStack(2, se3);
	    crashShutdown();
	}
	
	// Load the Initial values for Schedule-weighting-functions.	
	traceLog.log(INFO, 1, "startup", "Loading Schedule-weighting-function parameters:");
	try {
	    ServerContext.loadScheduleWeightingParams();
	} catch (ServerEnvException se3) {
	    errorLog.log(ERROR, 1, "Error loading schedule weights.", se3);
	    errorLog.dumpStack(2, se3);
	    crashShutdown();
	}
	traceLog.log(INFO, 1, "Oss", ossName, "startup", "********loaded scheduling********");

	WeightingParameters parms = Scheduling.getHeightFnParams();

	// TEMP reset the group overheads. this should be part of the ExecModel in the RCS...
	// and should be instrument dependant.
	//Group.NOMINAL_READOUT_TIME = 20000L;

	double s05 = Scheduling.calculateHeightFn(Math.toRadians(5.0));
	double s10 = Scheduling.calculateHeightFn(Math.toRadians(10.0));
	double s11 = Scheduling.calculateHeightFn(Math.toRadians(20.0));
	double s12 = Scheduling.calculateHeightFn(Math.toRadians(30.0));
	double s13 = Scheduling.calculateHeightFn(Math.toRadians(40.0));
	double s14 = Scheduling.calculateHeightFn(Math.toRadians(50.0));
	double s15 = Scheduling.calculateHeightFn(Math.toRadians(60.0));
	double s16 = Scheduling.calculateHeightFn(Math.toRadians(70.0));
	double s17 = Scheduling.calculateHeightFn(Math.toRadians(80.0));
	double s18 = Scheduling.calculateHeightFn(Math.toRadians(85.0));

	double s21 = Scheduling.calculateLatenessFn(1800000.0);
	double s22 = Scheduling.calculateLatenessFn(3600000.0);
	double s23 = Scheduling.calculateLatenessFn(7200000.0);
	double s24 = Scheduling.calculateLatenessFn(14400000.0);
	
	double s31 = Scheduling.calculateTransitFn(0.5);
	
	double s41 = Scheduling.calculateSlewFn(10000.0);
	double s42 = Scheduling.calculateSlewFn(30000.0);
	double s43 = Scheduling.calculateSlewFn(60000.0);
	double s44 = Scheduling.calculateSlewFn(120000.0);
	double s45 = Scheduling.calculateSlewFn(240000.0);
 


	traceLog.log(INFO, 1, "Oss", ossName, "startup", "Hgt = 05  degs: score "+s05+" OK: "+
		     parms.inRange(Math.toRadians(5.0)));
	traceLog.log(INFO, 1, "Oss", ossName, "startup", "Hgt = 10  degs: score "+s10+" OK: "+
		     parms.inRange(Math.toRadians(10.0)));
	traceLog.log(INFO, 1, "Oss", ossName, "startup", "Hgt = 20  degs: score "+s11+" OK: "+
		     parms.inRange(Math.toRadians(20.0)));
	traceLog.log(INFO, 1, "Oss", ossName, "startup", "Hgt = 30  degs: score "+s12+" OK: "+
		     parms.inRange(Math.toRadians(30.0)));
	traceLog.log(INFO, 1, "Oss", ossName, "startup", "Hgt = 40  degs: score "+s13+" OK: "+
		     parms.inRange(Math.toRadians(40.0)));
	traceLog.log(INFO, 1, "Oss", ossName, "startup", "Hgt = 50  degs: score "+s14+" OK: "+
		     parms.inRange(Math.toRadians(50.0)));
	traceLog.log(INFO, 1, "Oss", ossName, "startup", "Hgt = 60  degs: score "+s15+" OK: "+
		     parms.inRange(Math.toRadians(60.0)));
	traceLog.log(INFO, 1, "Oss", ossName, "startup", "Hgt = 70  degs: score "+s16+" OK: "+
		     parms.inRange(Math.toRadians(70.0)));
	traceLog.log(INFO, 1, "Oss", ossName, "startup", "Hgt = 80  degs: score "+s17+" OK: "+
		     parms.inRange(Math.toRadians(80.0)));
	traceLog.log(INFO, 1, "Oss", ossName, "startup", "Hgt = 85  degs: score "+s18+" OK: "+
		     parms.inRange(Math.toRadians(85.0)));
	
	traceLog.log(INFO, 1, "Oss", ossName, "startup", "Late = 1/2   hour: score "+s21);
	traceLog.log(INFO, 1, "Oss", ossName, "startup", "Late = 1     hour: score "+s22);
	traceLog.log(INFO, 1, "Oss", ossName, "startup", "Late = 2     hour: score "+s23);
	traceLog.log(INFO, 1, "Oss", ossName, "startup", "Late = 4     hour: score "+s24);

	traceLog.log(INFO, 1, "Oss", ossName, "startup", "Trn = 0.5 frac: score "+s31);

	traceLog.log(INFO, 1, "Oss", ossName, "startup", "Slw = 10   sec: score "+s41);
	traceLog.log(INFO, 1, "Oss", ossName, "startup", "Slw = 30   sec: score "+s42);
	traceLog.log(INFO, 1, "Oss", ossName, "startup", "Slw = 60   sec: score "+s43);
	traceLog.log(INFO, 1, "Oss", ossName, "startup", "Slw = 120  sec: score "+s44);
	traceLog.log(INFO, 1, "Oss", ossName, "startup", "Slw = 240  sec: score "+s45);
	
	// Load the initial values for the P2T client-settings.	
	traceLog.log(INFO, 1, "Oss", ossName, "startup", "Loading PEST Client-settings:");
	try {
	    ServerContext.loadClientSettings();
	} catch (ServerEnvException se3) {
	    errorLog.log(ERROR, 1, "Error loading client settings for PEST", se3);
	    errorLog.dumpStack(2, se3);
	    crashShutdown();
	}
	
	// Load telescope parameters.
	traceLog.log(INFO, 1, "Oss", ossName, "startup", "Loading Telescope-parameters:");
	try {
	    ServerContext.loadTelescopeParameters();
	} catch (ServerEnvException se3) {
	    errorLog.log(ERROR, 1, "Error loading telescope parameters.",se3);
	    errorLog.dumpStack(2, se3);
	    crashShutdown();
	}
	double latit = ServerContext.getTelescopeParameters().getSiteLatitude();
	double longit = ServerContext.getTelescopeParameters().getSiteLongitude();
	Position.setViewpoint(latit, longit);
	traceLog.log(INFO, 1, "Oss", ossName, "startup", 
		     "Set Position Viewpoint to: Long: "+Position.toDMSString(longit)+
		     " Lat: "+Position.toDMSString(latit));

	try {
	    Class.forName("ngat.astrometry.Astrometry");
	} catch (ClassNotFoundException cx) {
	    errorLog.log(ERROR, 1, "Error loading Astrometry.", cx);
	    errorLog.dumpStack(2, cx);
	    crashShutdown();
	}

	// ### TEST BLOCK ####
	double lst = JSlalib.getGMST(System.currentTimeMillis(), longit);
	System.err.println("The current LST according to JSlalib.getGMST() is: "+Position.toHMSString(lst));

	System.err.println("Loading Astrometry..");
	lst = Astrometry.computeLST(longit);
	System.err.println("Computed LST is"+ Position.toHMSString(lst));
	
	Position test = new Position(lst, Math.toRadians(10.0));
	 
	System.err.println("Position is: "+Position.toHMSString(lst));
	
	traceLog.log(INFO, 1, "Oss", ossName, "startup",
		     "Test Position with RA set to LST: "+Position.toHMSString(lst)+" Dec to 10.0 degs"+
		     "\n\tRA:      "+Position.toHMSString(test.getRA())+
		     "\n\tDec:     "+Position.toDMSString(test.getDec())+
		     "\n\tHA:      "+Position.toHMSString(test.getHA())+
		     "\n\tAz:      "+Position.toDegrees(test.getAzimuth(), 3)+
		     "\n\tAlt:     "+Position.toDegrees(test.getAltitude(), 3)+
		     "\n\tTransit: "+Position.toDegrees(test.getTransitHeight(), 3));
	// ### TEST BLOCK ####
	
	// Load Instrument Filter properties.
	traceLog.log(INFO, 1, "Oss", ossName, "startup", "Loading Instrument filter settings:");	
	File instFile = globalSettings.getInstrumentsFile(); 
	try {
	    TelescopeStatus.configureInstruments(instFile);
	} catch (IOException iox) {
	    errorLog.log(ERROR, 1, "Error loading Instrument filter settings.", iox);
	    errorLog.dumpStack(2, iox);
	    crashShutdown();
	} catch (IllegalArgumentException iax) { 
	    errorLog.log(ERROR, 1, "Error loading Instrument filter settings.", iax);
	    errorLog.dumpStack(2, iax);
	    crashShutdown();
	}
	
	// Create and open the database and hook it to the Oss.
	// We set the correct OSS_Util link to dbRootName here.
	try {
	    makeDatabase();
	} catch (ServerOpException soe3) {
	    errorLog.log(ERROR, 1, "Error creating database: ", soe3);
	    crashShutdown();
	}
    	
	Root root = OSS_Utilities.fetchRoot();
	if (System.getProperty("cache.db").equalsIgnoreCase("true")) {
	    OSS_Utilities.init(true);
	}

	// ------------------------------
	// Load charging model parameters.
	// ------------------------------

	chargingModel = new ChargingModel();
	File acfile = new File("config/accounting.properties");
	try {
	    chargingModel.configure(acfile);
	} catch (Exception ax) {
	    errorLog.log(ERROR, 1, "Error loading charge accounting information: "+ax);
	    crashShutdown();
	}


	// ------------------------------
	// Load any required Schedulers.
	// ------------------------------

	TestScheduler ts = new TestScheduler(root);
	System.err.println("Rdy, Init TS");
	ts.init(new ConfigurationProperties());// cjm bugfix
	System.err.println("Rdy, Configure TS");
	try {
	    ts.configure(new File("config/test_scheduler.properties"));
	    traceLog.log(INFO, 1, "Configured OPTIMAL-DESPATCH Scheduler");
	} catch (Exception e) {
	    System.err.println("ErorrL"+e);
	    errorLog.log(ERROR, 1, "Error loading TestScheduler settings .", e);
	    errorLog.dumpStack(2, e);
	    crashShutdown();
	}
	System.err.println("Ready to add OPTIMAL-DESPATCH Scheduler");
	Scheduling.setScheduler(ts);
	traceLog.log(INFO, 1, "Loaded OPTIMAL-DESPATCH Scheduler.");
	
	// Setup a schedule monitor.
	try {
	    schedMonitor = new DefaultScheduleMonitor();  
	    traceLog.log(INFO, 1, "Prepared DefaultScheduleMonitor OK");
	    Naming.rebind("rmi://localhost:1099/ScheduleMonitor", schedMonitor);
	    traceLog.log(INFO, 1, "Bound ScheduleMonitor");
	} catch (Exception dx) {
	    dx.printStackTrace();
	    traceLog.log(INFO, 1, "** WARNING - Failed to link DefaultScheduleMonitor");
	}
	
	// Ok now we setup a Despatcher test..
	//if (System.getProperty("TestDespatcher") != null) {
	    //
	    // try {  

	    //if(System.getSecurityManager() == null) {
	    //    System.setSecurityManager(new RMISecurityManager());
	    //}
	    //TestDespatcher td = new TestDespatcher(ts);
	    //Naming.rebind("rmi://localhost:1099/Despatcher", td);
	    //traceLog.log(INFO, 1, "Bound Despatcher...");
	    //System.err.println("Despatcher linked okay");
	    //} catch (Exception e) {
	    //e.printStackTrace();
	    //traceLog.log(INFO, 1, "** WARNING - Failed to link TestDespatcher");
	    //}
	    //}

	root = OSS_Utilities.fetchRoot();
	root.getName();

	try {
	    tscore = new TestContention(false); // non-simulated time for logs..
	    tscore.setRoot(root);
	    //tscore.setDomeLowLimit(ServerContext.getTelescopeParameters().getDomeLowLimit());
	    // let it read these from tea properties...
	    tscore.configure(new File("config/tea_scoring.properties"));
	    traceLog.log(INFO, 1, "Configured TEA Scoring Calculator");

	    if (System.getProperty("exec.model") != null) {
		traceLog.log(INFO, 1, "Lookup AvailabilityModel...");
		try {
		    AvailabilityModel am = (AvailabilityModel)Naming.lookup("rmi://localhost/AvailabilityModel");
		    traceLog.log(INFO, 1, "AvailabilityModel located: "+am);
		    // find all the windows over the next (arbitrary) 24 hours at 30 minute intervals.
		    long stt = System.currentTimeMillis();
		    long srct = stt;
		    while (srct < stt + 24*60*60*1000L) {
			boolean avail = am.isAvailable(srct);
			System.err.println("Schedulable window at: "+sdf.format(new Date(srct))+" "+avail);
			srct += 30*60*1000L;
		    }
    
		    List windows = am.getUnavailableTimes(0L,0L);
		    System.err.println("UA Windows are: "+windows);

		    tscore.setUnavailableWindows(windows);

		} catch (Exception e) {
		    e.printStackTrace();
		}
	    }

	    // load once then let loader handle this.
	    traceLog.log(INFO, 1, "Ready load TSCORE cache...");
	    tscore.loadCache();
	    traceLog.log(INFO, 1, "Ready TSCORE, load available windows...");
	    tscore.loadUnavailableWindows();

	} catch (Exception et) {	    
	    errorLog.log(ERROR, 1, "Error loading TEA Scoring Calculator settings .", et);
	    errorLog.dumpStack(2, et);
	    crashShutdown();
	}

	// Work out Tag/user priority normalization.
	traceLog.log(INFO, 1, "Working out Tag/user priority normalization factors..");
		   
	Iterator iTag = root.listAllTags();
	while (iTag.hasNext()) {
	    
	    int norm = 0;

	    Tag tag = (Tag)iTag.next();
	    tag.getName();
	
	    Iterator iUser = tag.listAllUsers();
	    while (iUser.hasNext()) {
	    
		User user = (User)iUser.next();
		user.getName();
		
		Iterator it = user.listAllProposals();
		while (it.hasNext()) {
		    
		    KVPair pair = (KVPair)it.next();
		    String name = (String)pair.getKey();
		    
		    //System.err.println("Looking for proposal: "+name);
		    
		    Proposal proposal = null;
		    try {
			proposal = OSS_Utilities.fetchProposal(new Path(user.getFullPath(), name));
		    } catch (Exception ex) {
			ex.printStackTrace();
			traceLog.log(INFO, 1, "Error reading proposal: "+user.getFullPath()+"/"+name);
		    }
		    
		    if (proposal != null) {
			norm += (proposal.getPriority() <=0 ? 1 : proposal.getPriority());
		    }
		}
	    }

	    traceLog.log(INFO, 1, "Total for: "+tag.getName()+" = "+norm);

	    // stick normalization for TAG into Scheduling cache.
	    Scheduling.addTagPriorityNormalizationFactor(tag.getName(), norm);

	} // next TAG

	// Prepare the fixed group cache.
	traceLog.log(INFO, 1, "Preparing FixedGroup times cache..");
		       	
	// Check it exists.
	//if (root == null) {		
	//  throw new IllegalArgumentException("Root not found");
	//} 

	
	iTag = root.listAllTags();
	while (iTag.hasNext()) {
	    
	    Tag tag = (Tag)iTag.next();
	    tag.getName();
	
	    Iterator iUser = tag.listAllUsers();
	    while (iUser.hasNext()) {
	    
		User user = (User)iUser.next();
		user.getName();
		
		Iterator it = user.listAllProposals();
		while (it.hasNext()) {
		    
		    KVPair pair = (KVPair)it.next();
		    String name = (String)pair.getKey();
		    
		    //System.err.println("Looking for proposal: "+name);
		    
		    Proposal proposal = null;
		    try {
			proposal = OSS_Utilities.fetchProposal(new Path(user.getFullPath(), name));
		    } catch (Exception ex) {
			ex.printStackTrace();
			traceLog.log(INFO, 1, "Error reading proposal: "+user.getFullPath()+"/"+name);
		    }
		    
		    if (proposal != null) {		
			
			Iterator ig = proposal.listAllGroups();
			while (ig.hasNext()) {
			    Group group = (Group)ig.next();
			    if (group instanceof FixedGroup) {
				Scheduling.modifyFixedGroup((FixedGroup)group);
			    }
			}
		    }
		}
	    }
	}

	traceLog.log(INFO, 1, "Testing FixedGroup times cache for earliest group at 1 hour intervals...");
	long now = System.currentTimeMillis();
	for (int itim = 0; itim < 16; itim++) {

	    long time = now + itim*3600*1000L;

	    FixedGroup fg = Scheduling.findNextFixedGroup(time, 0L);
	    if (fg != null) {
		traceLog.log(INFO, 1, "Oss", ossName, "startup", 
			     "Next FG after: "+sdf.format(new Date(time))+
			     " is: "+fg.getFullPath()+" at "+sdf.format(new Date(fg.getFixedTime())));
	    } else {
		traceLog.log(INFO, 1, "Oss", ossName, "startup", 
			     "There are no more FixedGroups after: "+sdf.format(new Date(time)));
	    }
	} 

	// --------------------------------------------------------------------
	// Compute cached metrics for each available group.
	// --------------------------------------------------------------------

	traceLog.log(INFO, 1, "Preparing metrics cache..");

	iTag = root.listAllTags();
        while (iTag.hasNext()) {

            Tag tag = (Tag)iTag.next();
            tag.getName();

            Iterator iUser = tag.listAllUsers();
            while (iUser.hasNext()) {

                User user = (User)iUser.next();
                user.getName();

                Iterator it = user.listAllProposals();
                while (it.hasNext()) {

                    KVPair pair = (KVPair)it.next();
                    String name = (String)pair.getKey();

		    Proposal proposal = null;
                    try {
                        proposal = OSS_Utilities.fetchProposal(new Path(user.getFullPath(), name));
                    } catch (Exception ex) {
                        ex.printStackTrace();
                        traceLog.log(INFO, 1, "Error reading proposal: "+user.getFullPath()+"/"+name);
                    }

                    if (proposal == null) 
			continue;
		    
		    // prune out any expired ones
		    
		    // Outside activation period.
		    if (proposal.getActivationDate() > now ||
			proposal.getExpiryDate() < now) {
			
			traceLog.log(1, "Pruning Proposal "+proposal.getName()+" as outside activation period: "+
				     sdf.format(new Date(proposal.getActivationDate()))+" - "+
				     sdf.format(new Date(proposal.getExpiryDate())));
			continue;
		    }
		    
		    Iterator ig = proposal.listAllGroups();
		    while (ig.hasNext()) {
			Group group = (Group)ig.next();
			if (group.getExpiryDate() < now)
			    continue;
			Scheduling.updateMetricsCache(group);
			
			double d = Scheduling.getMetricsCacheValue(group);
			traceLog.log(1, "Check metric for: "+group.getFullPath()+" MaxEl: "+Position.toDegrees(d,2));
		    }
		}
	    }
	}
    
	// --------------------------------------------------------------------
	// Create and start the TransactionManager (TMX) server and gc threads.	
	// --------------------------------------------------------------------
	String txName = globalSettings.getOssName()+
	    ":V: "+ globalSettings.getOssVersion()+
	    ":DTMS-TMX:"+globalSettings.getServerPrimaryCommandPort();

	// e.g. FT2_OSS:V:1.2:DTMS-TMX:8877
	
// 	tx = TransactionManager.createExecutorInstance(txName, 
// 						       managerId,
// 						       globalSettings.getServerPrimaryCommandPort());

// 	File txCfgFile = globalSettings.getDtmsConfigFile();
// 	try {
// 	    tx.configure(txCfgFile);
// 	    traceLog.log(INFO, 1, "Loaded TransactionExecutor DTMS and security settings.");
// 	} catch (IOException iox) {
// 	    errorLog.log(ERROR, 1, "Error loading TransactionManager config settings.", iox);
// 	    errorLog.dumpStack(2, iox);
// 	    crashShutdown();
// 	} catch (IllegalArgumentException iax) { 
// 	    errorLog.log(ERROR, 1, "Error loading TransactionManager config settings .", iax);
// 	    errorLog.dumpStack(2, iax);
// 	    crashShutdown();
// 	}
	
// 	tx.start();
	
// create a load of queues.
	Vector queues = new Vector();
	for (int tp = 0; tp < 3; tp++) {
	    Vector pq = new Vector();
	    queues.add(pq);
	    System.err.println("Create job queue: Q["+tp+"]");
	}

	// --------------------------------------------------------------------
	// Create and start the transaction executor (QT) thread.
	// --------------------------------------------------------------------	
	//qt = new QueryThread(tx);
	qt = new QueryThread(queues);
	qt.start();
	System.err.println("Query thread started...");
	

	// --------------------------------------------------------------------
	// Create and start the (JMS) TransactionServer.
	// --------------------------------------------------------------------
	String tsName = globalSettings.getOssName()+
	    ":V: "+globalSettings.getOssVersion()+
	    ":JMS-TS:"+globalSettings.getServerSecondaryCommandPort();

	// e.g. FT2_OSS:V:1.2:JMS-TS:7766

	try {	   
	    st = new TransactionServer(tsName, globalSettings.getServerSecondaryCommandPort(), queues);
	    traceLog.log(INFO, 1, "Starting JMS TransactionServer: "+st.getName()+" on port: "+globalSettings.getServerSecondaryCommandPort()+"...");
	    st.start();
	    traceLog.log(INFO, 1, "Started JMS TransactionServer: "+st.getName());
	} catch (IOException iox) {
	    iox.printStackTrace();
	    errorLog.log(ERROR, 1, "Error starting JMS TransactionServer: "+tsName, iox);
	    errorLog.dumpStack(2, iox);
	    crashShutdown();
	}
	
	System.err.println("Startup completed");
    
    } // (startup).
    
    
    /** Called to clean up when Oss has received a termination signal. */
    protected void shutdown() {

	traceLog.log(INFO, 1, "Closing Down TransactionManager");

	try {
	
	    //tx.stop();
	    qt.terminate();

	} finally {

	    // Close the database and terminate any transactions.
	    closeDatabase();
	   
	    // Close logging streams.
	    closeLoggingStreams();

	}
    } // End (shutdown).
    


    // ---------------------------------------------
    // Control creation and destruction of database.
    // --------------------------------------------

    /** Create a PSE database object to reference the PhaseII database and hook to ServerContext. */
    //##
    //## THIS IS ALL GUBBINS NOW WITH JYD..............
    //##
    //##
    public void makeDatabase() throws ServerOpException {
	databaseDir = globalSettings.getDatabaseFile();
	File dbFile = new File(databaseDir, "phase2.s0");	
	traceLog.log(INFO, 1, 
		     "OSS Phase2 Database: " + globalSettings.getDatabaseFile().getPath() + " located "+
		     "Size " + dbFile.length()+ " bytes");
	Transaction.current().abort();
	
	// Hook to environment.
	//ServerContext.setDatabase(database);
	OSS_Utilities.setDatabaseDir(globalSettings.getDatabaseFile()); // ?? needed ??
	OSS_Utilities.setRootName(dbRootName);
	traceLog.log(INFO, 1,
		     "OSS_Utilities Database hook made to: "+dbRootName);
	
    }   

    /** Closes down the database and saves to file. */
    public void closeDatabase() { 
	traceLog.log(INFO, 1, "Closing Database Session Down:");	
	DBRuntime.shutDown(false);	
	traceLog.log(INFO, 1, "Database Session Closed successfully:");
    }

  
    // ---------------------------------------------
    // Control opening and closing of logging files.
    // ---------------------------------------------

    /** Opens up the logging streams on the files specified by the GlobalSettings
     * reached via - globalSettings.getXXXLogFile() */
    public void openLoggingStreams() throws ServerOpException {
	errorLog       = LogManager.getLogger("ERROR");
	traceLog       = LogManager.getLogger("TRACE");
	trafficLog     = LogManager.getLogger("TRAFFIC");
	scheduleLog    = LogManager.getLogger("SCHEDULE");
	performanceLog = LogManager.getLogger("PERFORMANCE");
	traceLog.setUseGlobalHandlers(true);
	errorLog.setUseGlobalHandlers(true);
    }

    /** Saves the current logging file and closes down. */
    public void closeLoggingStreams() {	
	errorLog.close();       
	traceLog.close();         
	trafficLog.close();       
	scheduleLog.close();       
	performanceLog.close();       
    }
   
    // -------
    // Escape.
    // -------

    /** Called when a critical error occcurs <b>only during startup</b>. */
    private void crashShutdown() {
	errorLog.log(ERROR, 1, "Emergency shutdown");
	closeDatabase();
	closeLoggingStreams();
	System.exit(1);
    }
    
    /** Stop the OSS with supplied exitcode.
     * @param exitCode The exit code for the JVM.
     * One of: [ STOP | RESTART ].
     * ## TEMP: We just exit after a delay.
     * @param delay Delay before stopping (millis).
     */
    public void stop(int exitCode, long delay) {
	this.exitCode = exitCode;	
	traceLog.log(INFO, 1, "Calling shutdown with code: "+exitCode+" in "+(delay/1000)+" secs");

	final long d = delay;
	final int  e = exitCode;
	Runnable r = new Runnable() {
		public void run() {
		    try {Thread.sleep(d);} catch (InterruptedException x) {}
		    shutdown();
		    System.exit(e);
		}};
	(new Thread(r)).start();
    }

    /** Pause the TMX.*/
    public void pause() {}

    /** Restart TMX.*/
    public void restart() {}

    /** Returns the state of the Oss. If true, Oss is PAUSED. */ 
    public boolean isPaused() { return paused;}

    /** Returns a ref to the TEA scoring calculator.*/
    public TestContention getTeaScoringCalculator() { return tscore; }

    /** Returns a ref to the charging model.*/
    public ChargingModel getChargingModel() { return chargingModel; }

    /** Returns a ref to the schedule monitor.*/
    public DefaultScheduleMonitor getScheduleMonitor() { return schedMonitor; }

} // end class def [Oss].


/** $Log: Oss.java,v $
/** Revision 1.7  2007/08/24 08:01:28  snf
/** remoived setting NOM_RO time
/**
/** Revision 1.6  2007/07/05 11:49:24  snf
/** add bwm
/**
/** Revision 1.5  2007/02/27 11:49:09  snf
/** added exec model
/**
/** Revision 1.4  2007/02/06 09:04:35  snf
/** added TEA scoring calculator and accessors.
/**
/** Revision 1.3  2006/12/19 11:11:48  snf
/** Added temp fix for Group readout time for observations - should be in RCS ExecModel.
/**
/** Revision 1.2  2006/11/27 08:48:03  snf
/** Removed SMS refs.
/**
/** Revision 1.1  2006/11/27 08:45:00  snf
/** Initial revision
/**
/** Revision 1.1  2006/11/27 08:36:12  snf
/** Initial revision
/** */
















